package android.util;

public final class Pools {

  /**
   * Interface for managing a pool of objects.
   *
   * @param <T> The pooled type.
   */
  public static interface Pool<T> {

    /**
     * @return An instance from the pool if such, null otherwise.
     */
    public T acquire();

    /**
     * Release an instance to the pool.
     *
     * @param instance The instance to release.
     * @return Whether the instance was put in the pool.
     * @throws IllegalStateException If the instance is already in the pool.
     */
    public boolean release(T instance);
  }

  private Pools() {
    /* do nothing - hiding constructor */
  }

  /**
   * Simple (non-synchronized) pool of objects.
   *
   * @param <T> The pooled type.
   */
  public static class SimplePool<T> implements Pool<T> {
    private final Object[] mPool;

    private int mPoolSize;

    /**
     * Creates a new instance.
     *
     * @param maxPoolSize The max pool size.
     * @throws IllegalArgumentException If the max pool size is less than zero.
     */
    public SimplePool(int maxPoolSize) {
      if (maxPoolSize <= 0) {
        throw new IllegalArgumentException("The max pool size must be > 0");
      }
      mPool = new Object[maxPoolSize];
    }

    @Override
    @SuppressWarnings("unchecked")
    public T acquire() {
      if (mPoolSize > 0) {
        final int lastPooledIndex = mPoolSize - 1;
        T instance = (T) mPool[lastPooledIndex];
        mPool[lastPooledIndex] = null;
        mPoolSize--;
        return instance;
      }
      return null;
    }

    @Override
    public boolean release(T instance) {
      if (isInPool(instance)) {
        throw new IllegalStateException("Already in the pool!");
      }
      if (mPoolSize < mPool.length) {
        mPool[mPoolSize] = instance;
        mPoolSize++;
        return true;
      }
      return false;
    }

    private boolean isInPool(T instance) {
      for (int i = 0; i < mPoolSize; i++) {
        if (mPool[i] == instance) {
          return true;
        }
      }
      return false;
    }
  }

  /**
   * Synchronized pool of objects.
   *
   * @param <T> The pooled type.
   */
  public static class SynchronizedPool<T> extends SimplePool<T> {
    private final Object mLock;

    /**
     * Creates a new instance.
     *
     * @param maxPoolSize The max pool size.
     * @param lock        an optional custom object to synchronize on
     * @throws IllegalArgumentException If the max pool size is less than zero.
     */
    public SynchronizedPool(int maxPoolSize, Object lock) {
      super(maxPoolSize);
      mLock = lock;
    }

    /**
     * @see #SynchronizedPool(int, Object)
     */
    public SynchronizedPool(int maxPoolSize) {
      this(maxPoolSize, new Object());
    }

    @Override
    public T acquire() {
      synchronized (mLock) {
        return super.acquire();
      }
    }

    @Override
    public boolean release(T element) {
      synchronized (mLock) {
        return super.release(element);
      }
    }
  }
}